package org.nabc.core;

import com.mysql.cj.jdbc.ConnectionImpl;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.nabc.support.PropertyValue;
import org.nabc.support.PropertyValues;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.*;

@Slf4j
public class DataBaseOperation extends AbstractStringOperation {
    /**
     * 将数据库连接下的所有表转化为 PropertyValues 值
     *
     * @param connection
     * @return
     */
    @SneakyThrows
    public List<PropertyValues> readTableInfo(Connection connection, String... tablePrefix) {
        String dbName = ((ConnectionImpl) connection).getDatabase();

        ResultSet tables = executeQuery(connection, "SELECT TABLE_NAME, TABLE_COMMENT " +
                "FROM information_schema.TABLES " +
                "WHERE TABLE_SCHEMA = '" + dbName + "'");
        ResultSet size = executeQuery(connection, "SELECT COUNT(TABLE_NAME) " +
                "FROM information_schema.TABLES " +
                "WHERE TABLE_SCHEMA = '" + dbName + "'");

        try {
            return handleTableInfoByThread(tables, size, connection, dbName, tablePrefix);
        } catch (Exception e) {
            return null;
        }
    }

    private List<PropertyValues> handleTableInfoByThread(ResultSet tables, ResultSet size,
                                                        Connection connection, String dbName,
                                                        String... tablePrefix) throws SQLException, InterruptedException {
        List<PropertyValues> propertyValuesList = new ArrayList<>();

        ExecutorService service = new ThreadPoolExecutor(10, 10, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingDeque<>());
        size.next();
        CountDownLatch countDownLatch = new CountDownLatch(size.getInt(1));

        while (tables.next()) {
            String tableName = tables.getString(1);
            String tableComment = tables.getString(2);
            service.submit(() -> {
                try {
                    log.info("正在读取表 {}", tableName);
                    PropertyValues propertyValues = PropertyValues
                            .builder()
                            .primitiveTableName(tableName)
                            .tableName(getTableName(tableName, tablePrefix))
                            .tableComment(tableComment)
                            .propertyValues(readColumnInfo(connection, dbName, tableName))
                            .build();
                    propertyValuesList.add(propertyValues);
                    log.info("表 {} 读取完成", tableName);
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    countDownLatch.countDown();
                }
            });
        }
        countDownLatch.await();

        return propertyValuesList;
    }

    private List<PropertyValue> readColumnInfo(Connection connection, String dbName, String tableName) throws SQLException {
        ResultSet columns = executeQuery(connection, "SELECT * FROM information_schema.COLUMNS " +
                "WHERE table_schema = '" + dbName + "' and table_name = '" + tableName + "';");
        List<PropertyValue> propertyValues = new ArrayList<>();

        while (columns.next()) {
            PropertyValue propertyValue = PropertyValue
                    .builder()
                    .primitiveColumnName(columns.getString("column_name"))
                    .columnName(conversionHump(columns.getString("column_name")))
                    .fieldName(columns.getString("column_name"))
                    .columnComment(columns.getString("column_comment"))
                    .defaultValue(columns.getString("column_default"))
                    .isNotNull(columns.wasNull())
                    .isPrimaryKey(columns.getString("column_key").equals("PRI"))
                    .type(columns.getString("data_type"))
                    .build();
            propertyValues.add(propertyValue);
        }

        return propertyValues;
    }

    private ResultSet executeQuery(Connection connection, String sql) throws SQLException {
        return connection.createStatement().executeQuery(sql);
    }
}
